﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI.WebControls;
using System.Web.UI;
using System.Web.DynamicData;
using System.Web.Security;
using System.Configuration;
using NotAClue.ComponentModel.DataAnnotations;

namespace NotAClue.Web.DynamicData
{
    /// <summary>
    /// Secures the link button when used for delete actions
    /// </summary>
    public class SecureLinkButton : LinkButton
    {
        private const String IGNORE_BUTTON_NAMES = "SecureLinkButtonIgnorButtonNames";
        private String[] ignoreButtons = new String[] { "cancel", "jump", "refresh", "sort" };

        // default button mappings
        private static Dictionary<String, List<String>> buttonMappings = new Dictionary<string, List<String>>()
            {
                {"Details",new List<String>(){"details"}},
                {"Edit",new List<String>(){"edit"}},
                {"Delete",new List<String>(){"delete"}},
                {"None",new List<String>(){"update","save","cancel"}}, // ??? Why none ????
                {"Insert",new List<String>(){"insert"}},
            };

        public static Dictionary<String, List<String>> ButtonMappings
        {
            get { return buttonMappings; }
            set { buttonMappings = value; }
        }

        /// <summary>
        /// Raises the <see cref="E:System.Web.UI.Control.Init"/> event.
        /// </summary>
        /// <param name="e">
        /// An <see cref="T:System.EventArgs"/> 
        /// object that contains the event data.
        /// </param>
        protected override void OnInit(EventArgs e)
        {
            if (ConfigurationManager.AppSettings.AllKeys.Contains(IGNORE_BUTTON_NAMES))
                ignoreButtons = ConfigurationManager.AppSettings[IGNORE_BUTTON_NAMES]
                    .ToLower()
                    .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

            base.OnInit(e);
        }

        protected override void OnPreRender(EventArgs e)
        {
            if (IsHidden())
                Visible = false;

            base.OnPreRender(e);
        }

        /// <summary>
        /// Renders the control to the specified HTML writer.
        /// </summary>
        /// <param name="writer">
        /// The <see cref="T:System.Web.UI.HtmlTextWriter"/> 
        /// object that receives the control content.
        /// </param>
        protected override void Render(HtmlTextWriter writer)
        {
            if (!IsHidden())
                base.Render(writer);
            //else
            //    writer.Write(String.Format("<a>{0}</a>", Text));
        }

        /// <summary>
        /// Determines whether this instance is disabled.
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if this instance is 
        /// 	disabled; otherwise, <c>false</c>.
        /// </returns>
        private Boolean IsHidden()
        {
            var buttonAction = String.Empty;

            if (!String.IsNullOrEmpty(CommandName) && !ignoreButtons.Contains(CommandName.ToLower()))
                buttonAction = MapToActionString(CommandName);
            else
                buttonAction = MapToActionString(Text);

            // get restrictions for the current
            // users access to this table
            var parent = this.GetContainerControl<DataBoundControl>();
            MetaTable table = null;
            try
            {
                if (parent != null)
                    table = ((EntityDataSource)parent.DataSourceObject).GetTable();
                else
                    table = DynamicDataRouteHandler.GetRequestMetaTable(Context);
            }
            catch
            {
                return false;
            }

            if (table == null)
                return false;

            var usersRoles = Roles.GetRolesForUser();

            // get ALL hide secure button attributes
            var hiddenButtons = table.Attributes.OfType<HideSecureButtonAttribute>();

            if (hiddenButtons.Count() > 0)
            {
                foreach (var hb in hiddenButtons)
                {
                    // the LinkButton is considered disabled if Action is not allowed.
                    try
                    {
                        var button = buttonAction.ToEnum<SecureButtons>();

                        var hasRole = hb.HasAnyRole(usersRoles);
                        if (hasRole && (hb.Buttons & button) == button)
                            return true;
                    }
                    catch { }
                }
            }

            // get ALL secure table attributes
            var tableRestrictions = table.Attributes.OfType<SecureTableAttribute>();

            if (tableRestrictions.Count() > 0)
            {
                foreach (var tp in tableRestrictions)
                {
                    // the LinkButton is considered disabled if Action is not allowed.
                    var action = buttonAction.ToTitleCase().ToEnum<TableActions>();
                    if (action == TableActions.None)
                        return false;

                    var hasRole = tp.HasAnyRole(usersRoles);
                    if (hasRole && (tp.Actions & action) == action)
                        return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Maps the name to an action string.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns>Action string</returns>
        /// <remarks>Maps the name passed in to the appropriate action string.</remarks>
        private String MapToActionString(String name)
        {
            foreach (var item in buttonMappings)
            {
                if (item.Value.Contains(name.ToLower()))
                    return item.Key;
            }
            // if no action found then map to action none.
            return TableActions.None.ToString();
        }
    }
}
